package com.blackhorse.mybatis.utils;

import com.blackhorse.mybatis.annotation.Select;
import com.blackhorse.mybatis.config.Configuration;
import com.blackhorse.mybatis.config.Mapper;
import com.blackhorse.mybatis.io.Resources;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class XMLConfigBuilder {

    /**
     * 使用dom4j解析mybatis核心配置文件并将解析数据存入Configuration类
     *
     * @param config
     * @return
     */
    public static Configuration loadConfiguration(InputStream config) {
        Configuration cfg = new Configuration();
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(config);
            Element root = document.getRootElement();
            List<Element> properties = root.selectNodes("//property");
            for (Element property : properties) {
                String name = property.attributeValue("name");
                if ("driver".equals(name)) {
                    String driver = property.attributeValue("value");
                    cfg.setDriver(driver);
                }
                if ("url".equals(name)) {
                    String url = property.attributeValue("value");
                    cfg.setUrl(url);
                }
                if ("username".equals(name)) {
                    String username = property.attributeValue("value");
                    cfg.setUsername(username);
                }
                if ("password".equals(name)) {
                    String password = property.attributeValue("value");
                    cfg.setPassword(password);
                }
            }

            List<Element> mappers = root.selectNodes("//mappers/mapper");
            for (Element mapper : mappers) {
                Attribute attribute = mapper.attribute("resource");
                if (attribute != null) {
                    System.out.println("使用xml配置");
                    String mapperPath = attribute.getValue();

                    Map<String, Mapper> mapperMap = loadMapperConfiguration(mapperPath);
                    cfg.setMappers(mapperMap);
                } else {
                    System.out.println("使用注解配置");
                    String daoClassPath = mapper.attributeValue("class");
                    Map<String, Mapper> mapperMap = loadMapperAnnotationConfiguration(daoClassPath);
                    cfg.setMappers(mapperMap);
                }
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }


        return cfg;
    }

    /**
     * 通过注解得到sql语句和resuLtType
     * 并封装成map集合
     * @param daoClassPath
     * @return
     */
    private static Map<String, Mapper> loadMapperAnnotationConfiguration(String daoClassPath) {
        Map<String, Mapper> mappers = new HashMap<String, Mapper>();
        try {
            Class daoClass = Class.forName(daoClassPath);
            Method[] methods = daoClass.getMethods();
            for (Method method : methods) {
                boolean isAnnotated = method.isAnnotationPresent(Select.class);
                if (isAnnotated) {
                    Mapper mapper = new Mapper();
                    Select selectAnno = method.getAnnotation(Select.class);
                    String queryString = selectAnno.value();
                    mapper.setQueryString(queryString);
                    Type type = method.getGenericReturnType();//List<User>
                    if (type instanceof ParameterizedType) {
                        ParameterizedType ptype = (ParameterizedType) type;
                        Type[] types = ptype.getActualTypeArguments();//User
                        Class domainClass = (Class) types[0];
                        String resultType = domainClass.getName();
                        mapper.setResultType(resultType);
                    }
                    String methodName = method.getName();
                    String className = method.getDeclaringClass().getName();
                    String key = className + "." + methodName;
                    mappers.put(key, mapper);
                }
            }
            return mappers;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解析mapper配置文件
     * 将文件中每个sql语句的id(namespace.id)为key，Mapper（包含sql语句和resultType）为value存入Map集合
     *
     * @param mapperPath
     * @return
     */
    private static Map<String, Mapper> loadMapperConfiguration(String mapperPath) {
        InputStream in = null;
        Map<String, Mapper> mapperMap = new HashMap<String, Mapper>();
        //System.out.println(mapperPath);

        in = Resources.getResourceAsStream(mapperPath);
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(in);
            Element root = document.getRootElement();
            String namespace = root.attributeValue("namespace");
            List<Element> selectElements = root.selectNodes("//select");

            for (Element selectElement : selectElements) {
                String id = selectElement.attributeValue("id");
                String resultType = selectElement.attributeValue("resultType");
                String queryString = selectElement.getText();
                String key = namespace + "." + id;
                Mapper mapper = new Mapper();
                mapper.setQueryString(queryString);
                mapper.setResultType(resultType);
                mapperMap.put(key, mapper);
            }
            return mapperMap;
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }
}
